package com.studerb.tasks.headers;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Future;

import org.apache.commons.io.IOUtils;
import org.apache.commons.net.io.DotTerminatedMessageReader;
import org.apache.commons.net.nntp.NNTPReply;
import org.springframework.beans.factory.annotation.Autowired;

import com.studerb.model.Header;
import com.studerb.model.NewsGroup;
import com.studerb.service.interfaces.IHeaderService;
import com.studerb.service.interfaces.INewsGroupService;
import com.studerb.service.interfaces.ISettingsService;
import com.studerb.service.interfaces.ITaskService;
import com.studerb.tasks.AbstractTask;
import com.studerb.util.Constants;

public class XoverAllCommandTask extends AbstractTask<Void, Void> {
	@Autowired protected ISettingsService settingsService;
	@Autowired protected IHeaderService headerService;
	@Autowired protected INewsGroupService newsGroupService;
	@Autowired protected ITaskService taskService;
	protected List<Header> tempHeaders;
	protected NewsGroup newsGroup;
	protected int DB_BATCH_SIZE;
	protected int XOVER_BATCH_SIZE;
	protected BlockingQueue<String> queue;
	Future consumer;
	protected int current;

	private void doGroupCommand() throws IOException {
		setMessage("Sending Group Command");
		if (newsGroup == null) {
			throw new IllegalStateException("Must set newsgroup before executing");
		}
		groupCommandReply = nntpConnection.groupCommand(newsGroup.getGroupName());
		logger.debug("Group command reply: " + nntpConnection.getReplyString());
	}

	@Override protected Void doInBackground() throws Exception {
		logger.debug("Starting do In background");
		doGroupCommand();
		int first = groupCommandReply.getFirstPost();
		int last = groupCommandReply.getLastPost();
		setTitle("Downloading " + (last - first) + " New Headers: " + newsGroup.getGroupName());
		logger.debug("First: " + first + "  / Last: " + last);
		current = first;
		int start;
		int end;
		logger.debug("Starting xover consumer");
		try {
			consumer = taskService.submit(headerService.getXoverCommandProcessor(queue, newsGroup));
			while (current <= last && !isCancelled() && !consumer.isDone()) {
				start = current;
				end = current + XOVER_BATCH_SIZE > last ? last : current + XOVER_BATCH_SIZE;
				downloadBatch(start, end);
				current = end + 1;
				logger.debug("Current: " + current);
			}
			setMessage("Finished Downloading - Waiting on Database");
		}
		finally {
			queue.put(Constants.END_OF_QUEUE);
		}
		consumer.get();
		return null;
	}

	private void downloadBatch(int start, int end) throws Exception {
		logger.debug(String.format("Downloading Headers %d - %d", start, end));
		setMessage(String.format("Downloading Headers %d - %d", start, end));
		BufferedReader reader = new BufferedReader(new DotTerminatedMessageReader(nntpConnection.getReader()));
		String tempLine;
		int loopCount = 0;
		try {
			String range = start + "-" + end;
			int reply = nntpConnection.xover(range);
			if (reply != NNTPReply.XOVER_INFORMATION_FOLLOWS) {
				throw new IOException("Bad XOVER reply: " + nntpConnection.getReplyString());
			}
			while (!isCancelled() && !consumer.isDone()) {
				tempLine = reader.readLine();
				if (tempLine == null) {
					break;
				}
				else {
					queue.put(tempLine);
					setProgress(start + loopCount, groupCommandReply.getFirstPost(), groupCommandReply.getLastPost());
					loopCount++;
				}
			}
			logger.debug("Downloaded: " + loopCount + " header lines");
			newsGroup.setLastUpdatedHeaders(Calendar.getInstance());
			newsGroupService.updateNewsGroup(newsGroup);
		}
		finally {
			logger.info(String.format("Downloaded %d header lines.", loopCount));
			IOUtils.closeQuietly(reader);
		}
	}

	@Override public void init() {
		DB_BATCH_SIZE = Integer.parseInt(settingsService.getSetting("db.batchsize"));
		XOVER_BATCH_SIZE = Integer.parseInt(settingsService.getSetting("tasks.xoverAllCommandTask.batchSize"));
		queue = new ArrayBlockingQueue<String>(XOVER_BATCH_SIZE);
		title = "Downloading All Headers";
		determinable = true;
		connectionNeeded = true;
		tempHeaders = new ArrayList<Header>(DB_BATCH_SIZE);
	}

	public void setNewsGroup(NewsGroup newsGroup) {
		this.newsGroup = newsGroup;
	}

}
